package liming.tool.pool;

import liming.tool.lock.CodeCondition;
import liming.tool.lock.CodeLock;
import liming.tool.lock.LockAtomicBoolean;
import liming.tool.lock.LockAtomicLong;
import liming.tool.pool.PoolPolicy.PutPolicy;
import liming.tool.pool.PoolPolicy.TakePolicy;

import java.util.*;
import java.util.concurrent.TimeUnit;

public class ObjectsPool<T> {
    // 核心字段
    private final List<T> list; // 存储池
    private final int maxSize; // 最大容量
    private final CodeLock codeLock; // 锁
    private final CodeCondition notFull; // 满
    private final CodeCondition notEmpty; // 空

    // 统计字段
    private final LockAtomicLong putCounter; // 放入
    private final LockAtomicLong takeCounter; // 取出
    private final LockAtomicBoolean closed; // 是否关闭

    // 策略字段
    private final PutPolicy putPolicy; // 放入策略
    private final TakePolicy takePolicy; // 取出策略

    private PoolEventInterface<T> poolEventInterface;
    private final LockAtomicBoolean eventing;

    public boolean isEventing() {
        return eventing.get();
    }

    void setPoolEvent(PoolEventInterface<T> poolEventInterface) {
        this.poolEventInterface = poolEventInterface;
        eventing.set(true);
    }
    void removePoolEvent(){
        eventing.set(false);
        this.poolEventInterface = null;
    }

    /**
     * 构造方法
     *
     * @param codeLock   同步锁
     * @param list       存储池
     * @param maxSize    最大容量
     * @param putPolicy  放入策略
     * @param takePolicy 取出策略
     */
    public ObjectsPool(CodeLock codeLock,
                       List<T> list,
                       int maxSize,
                       PutPolicy putPolicy,
                       TakePolicy takePolicy) {
        this.codeLock = codeLock;
        this.list = new LinkedList<>(list); // 防御性拷贝
        this.maxSize = maxSize;
        this.putPolicy = putPolicy;
        this.takePolicy = takePolicy;
        this.notFull = codeLock.newCondition();
        this.notEmpty = codeLock.newCondition();

        putCounter = new LockAtomicLong(codeLock).set(list.size());
        takeCounter = new LockAtomicLong(codeLock);
        closed = new LockAtomicBoolean(codeLock);

        eventing = new LockAtomicBoolean(codeLock);
    }

    //Public API
    public Result put(T object) {
        return codeLock.lock(() -> doPut(object, -1, null));
    }

    public Result put(T object, long timeout, TimeUnit unit) {
        return codeLock.lock(() -> doPut(object, timeout, unit));
    }

    public Result take() {
        return codeLock.lock(() -> doTake(-1, null));
    }

    public Result take(long timeout, TimeUnit unit) {
        return codeLock.lock(() -> doTake(timeout, unit));
    }

    // 状态查询方法
    public long getPutCount() {
        return putCounter.get();
    }

    public long getTakeCount() {
        return takeCounter.get();
    }

    public long getNowSize() {
        return codeLock.lock(list::size);
    }

    public List<T> getList() {
        return codeLock.lock(() -> new ArrayList<>(list));
    }

    public boolean isClosed() {
        return closed.get();
    }

    public void close() {
        codeLock.lock(() -> {
            event(PoolEventEnum.close_before, null);
            closed.set(true);
            notFull.signalAll();
            notEmpty.signalAll();
            event(PoolEventEnum.close_after, null);
        });
    }
    // ObjectsPool.java 新增方法
    public void reopen() {
        event(PoolEventEnum.reopen_before, null);
        codeLock.lock(() -> {
            if (!closed.get()) return;
            closed.set(false);
            eventing.set(poolEventInterface != null);
            notFull.signalAll();
            notEmpty.signalAll();
            event(PoolEventEnum.reopen_after, null);
        });
    }

    public boolean isEmpty() {
        return codeLock.lock(list::isEmpty);
    }

    public boolean isFull() {
        return maxSize > 0 && codeLock.lock(() -> list.size() >= maxSize);
    }

    // 核心实现逻辑 在一个锁下
    private Result doPut(T object, long timeout, TimeUnit unit) {
        long deadline = timeout > 0 ?
                System.currentTimeMillis() + unit.toMillis(timeout) : -1;
        try {
            event(PoolEventEnum.put_before,object);
            while (!isClosed()) {
                // 前置快速检查
                if (!isFull()) {
                    putCounter.add(1);
                    list.add(object);
                    notEmpty.signal();
                    event(PoolEventEnum.put_success,object);
                    return new Result(Status.SUCCESS, object);
                }

                switch (putPolicy) {
                    case BLOCK: { // 阻塞策略
                        Status status = awaitCodeCondition(notFull, deadline);
                        if (status != Status.SUCCESS) {
                            event(PoolEventEnum.put_fail,object);
                            return new Result(status, object); // 返回等待结果
                        }
                        break; // 继续循环检查
                    }

                    case DISCARD: { // 丢弃策略
                        if (deadline <= 0) { // 无超时立即返回
                            event(PoolEventEnum.put_fail,object);
                            return new Result(Status.FAIL, object);
                        }

                        // 带超时等待
                        Status status = awaitCodeCondition(notFull, deadline);
                        if (status == Status.SUCCESS && !isFull()) {
                            // 等待成功后再次检查
                            putCounter.add(1);
                            list.add(object);
                            notEmpty.signal();
                            event(PoolEventEnum.put_success,object);
                            return new Result(Status.SUCCESS, object);
                        }
                        return new Result(status, object); // 返回TIMEOUT/INTERRUPT
                    }

                    case REMOVE_OLDEST: { // 替换最旧元素
                        if (deadline <= 0) { // 无超时直接替换
                            putCounter.add(1);
                            T removed = list.remove(0);
                            list.add(object);
                            notEmpty.signal();
                            event(PoolEventEnum.put_success,object);
                            return new Result(Status.SUCCESS, removed);
                        }

                        // 带超时等待
                        Status status = awaitCodeCondition(notFull, deadline);
                        if (status == Status.TIMEOUT) {
                            putCounter.add(1);
                            T removed = list.remove(0);
                            list.add(object);
                            notEmpty.signal();
                            event(PoolEventEnum.put_success,object);
                            return new Result(Status.TIMEOUT, removed);
                        }
                        if (status != Status.SUCCESS) {
                            event(PoolEventEnum.put_fail,object);
                            return new Result(status, object);
                        }
                        break; // 继续循环检查
                    }

                    case REMOVE_NEWEST: { // 补全最新元素替换
                        if (deadline <= 0) { // 无超时直接替换
                            putCounter.add(1);
                            T removed = list.remove(list.size() - 1);
                            list.add(object);
                            notEmpty.signal();
                            event(PoolEventEnum.put_success,object);
                            return new Result(Status.SUCCESS, removed);
                        }

                        // 带超时等待
                        Status status = awaitCodeCondition(notFull, deadline);
                        if (status == Status.TIMEOUT) {
                            putCounter.add(1);
                            T removed = list.remove(list.size() - 1);
                            list.add(object);
                            notEmpty.signal();
                            event(PoolEventEnum.put_success,object);
                            return new Result(Status.TIMEOUT, removed);
                        }
                        if (status != Status.SUCCESS) {
                            event(PoolEventEnum.put_fail,object);
                            return new Result(status, object);
                        }
                        break; // 继续循环检查
                    }
                }

                // 重新检查关闭状态（可能在等待期间被关闭）
                if (isClosed()) {
                    event(PoolEventEnum.put_fail,object);
                    return new Result(Status.CLOSED, object);
                }
            }
            event(PoolEventEnum.put_fail,object);
            return new Result(Status.CLOSED, object); // 循环退出说明池已关闭
        }finally {
            event(PoolEventEnum.put_after,object);
        }

    }

    private Result doTake(long timeout, TimeUnit unit) {
        long deadline = timeout > 0 ?
                System.currentTimeMillis() + unit.toMillis(timeout) : -1;
        T value = null;
        try {
            event(PoolEventEnum.take_before,null);
            while (true) {
                if (isClosed() && isEmpty()) {
                    event(PoolEventEnum.take_fail,null);
                    return new Result(Status.CLOSED, null);
                }
                if (!isEmpty()) {
                    value = list.remove(0);
                    takeCounter.add(1);
                    notFull.signal();
                    event(PoolEventEnum.take_success,value);
                    return new Result(Status.SUCCESS, value);
                }
                if (isClosed()) {
                    event(PoolEventEnum.take_fail,value);
                    return new Result(Status.CLOSED, null);
                }

                switch (takePolicy) {
                    case BLOCK:
                        Status status = awaitCodeCondition(notEmpty, deadline);
                        if (Objects.requireNonNull(status) != Status.SUCCESS){
                            event(PoolEventEnum.take_fail,null);
                            return new Result(status, null);
                        }
                        break;
                    case RETURN_NULL:
                        event(PoolEventEnum.take_fail,null);
                        return new Result(Status.FAIL, null);
                }
            }
        }finally {
            event(PoolEventEnum.take_after,value);
        }
    }

    // 等待辅助方法

    /**
     * @return SUCCESS - 在规定时间内睡醒，INTERRUPT - 中断，TIMEOUT - 超时
     */
    private Status awaitCodeCondition(CodeCondition codeCondition, long deadline) {
        if (isClosed()) return Status.CLOSED;
        switch (deadline == -1 ? codeCondition.await() : codeCondition.awaitUntil(new Date(deadline))) {
            case NORMAL:
                return Status.SUCCESS;
            case INTERRUPT:
                return Status.INTERRUPT;
            case TIMEOUT:
                return Status.TIMEOUT;
        }
        if (isClosed()) return Status.CLOSED;
        return Status.FAIL;
    }

    private void event(PoolEventEnum event,T value){
        if(isEventing()){
            poolEventInterface.onEvent(event,this,value);
            if(event==PoolEventEnum.put_success&&isFull())
                poolEventInterface.onEvent(PoolEventEnum.full,this,null);
            if(event==PoolEventEnum.take_success&&isEmpty())
                poolEventInterface.onEvent(PoolEventEnum.empty,this,null);
        }
    }

    @Override
    public String toString() {
        return "ObjectsPool{" +
                "list=" + list +
                ", maxSize=" + maxSize +
                ", codeLock=" + codeLock +
                ", putCounter=" + putCounter +
                ", takeCounter=" + takeCounter +
                ", closed=" + closed +
                ", putPolicy=" + putPolicy +
                ", takePolicy=" + takePolicy +
                '}';
    }

    // 状态枚举
    public enum Status {
        SUCCESS,    // 操作成功
        FAIL,       // 立即失败（策略不允许等待）
        TIMEOUT,    // 超时
        CLOSED,     // 池已关闭
        INTERRUPT   // 线程中断
    }

    // 取结果封装
    public class Result {
        private final Status status;
        private final T object;

        public Result(Status status, T object) {
            this.status = status;
            this.object = object;
        }

        public Status status() {
            return status;
        }

        public T object() {
            return object;
        }

        @Override
        public String toString() {
            return "Result{" +
                    "status=" + status +
                    ", object=" + object +
                    '}';
        }
    }
}